<html>
	<head>
		<script type="text/javascript">
			var canvas;
			var context;
			var p = new Array(256);

			function main()
			{
				for ( var i = 0; i < p.length; ++ i )
				{
					p[i] = Math.floor(Math.random() * 255);
				}
				
				canvas = document.getElementById('perlinCanvas');
				context = canvas.getContext('2d');

				canvas.width = 256;
				canvas.height = 256;

				if (window.innerHeight < 512)
				canvas.height = window.innerHeight;

				if (window.innerWidth < 512)
				canvas.width = window.innerWidth;

				// Have the update function called every 16 ms
				//setInterval("update()", 16);

				draw();
			}

			function update()
			{
				// Move objects
				// Check collisions

				// Draw
				draw();
			}

			function draw()
			{
				if (canvas.getContext)
				{
					var imageData = context.createImageData(canvas.width, canvas.height);
					//var imageData = context.getImageData(0, 0, canvas.width, canvas.height);

					for (var x = 0; x < canvas.width; x++)
					{
						for (var y = 0; y < canvas.height; y++)
						{
							// calculate the Perlin Noise result
//							var result = noise2d(x / 40, y / 40);
							var result = fBm2d(x / canvas.width * 10, y / canvas.height * 10, 8);
//							var result = turbulence2d(x / canvas.width * 10, y / canvas.height * 10, 8);
//							var result = ridgedmf2d(x / canvas.width * 10, y / canvas.height * 10, 8);

							// convert the result from [-1, 1] to [0, 1]
							result = result * 0.5 + 0.5;

							// convert the result from [0, 1] to [0, 255]
							result = result * 255;

							var index = (x + y * canvas.width) * 4;
							imageData.data[index    ] = result;   //Red
							imageData.data[index + 1] = result;   //Green
							imageData.data[index + 2] = result;   //Blue
							imageData.data[index + 3] = 255;      //Alpha
						}
					}

					context.putImageData(imageData, 0, 0);

					//context.font = "20pt Arial";
					context.fillStyle = "rgba(255,255,255,1.0)";
					context.fillText("Hello World!", 10, 10);
				}
			}

			function onKeyDown(e)
			{
				alert("You pressed " + e.keyCode);
			}

			// Draws a rectangle at the given x and y pixel position filled with the given color.
			// The color is in the format "rgba(255,255,255,1.0)" (including the quotes!)
			function drawRectangle(x, y, width, height, color)
			{
				context.fillStyle = color;
				context.fillRect(x, y, width, height);
			}

			// 1 pixel rectangle - SLOW!
			// Colors the pixel at the provided x and y pixel position with the given color
			// The color is in the format "rgba(255,255,255,1.0)"
			function colorPixel(x, y, color)
			{
				context.fillStyle = color;
				context.fillRect(x, y, 1, 1);
			}

			function noise2d(x, y)
			{
				var X = Math.floor(x) & 255;
				var Y = Math.floor(y) & 255;

				x -= Math.floor(x);
				y -= Math.floor(y);

				var u = fade(x);
				var v = fade(y);

				var A = p[X  ]+Y;
				var AA = p[A];
				var AB = p[A+1];

				var B = p[X+1]+Y;
				var BA = p[B];
				var BB = p[B+1];

				return lerp(v, lerp(u, grad(p[AA  ], x  , y  , 0),
				grad(p[BA  ], x-1, y  , 0)),
				lerp(u, grad(p[AB  ], x  , y-1, 0),
				grad(p[BB  ], x-1, y-1, 0)));
			}

			function noise3d(x, y, z)
			{
				var X = Math.floor(x) & 255;                  // FIND UNIT CUBE THAT
				var Y = Math.floor(y) & 255;                  // CONTAINS POINT.
				var Z = Math.floor(z) & 255;

				x -= Math.floor(x);                                // FIND RELATIVE X,Y,Z
				y -= Math.floor(y);                                // OF POINT IN CUBE.
				z -= Math.floor(z);

				var u = fade(x);                                // COMPUTE FADE CURVES
				var v = fade(y);                                // FOR EACH OF X,Y,Z.
				var w = fade(z);

				var A = p[X  ]+Y;                        // HASH COORDINATES OF
				var AA = p[A]+Z;                          // THE 8 CUBE CORNERS,
				var AB = p[A+1]+Z;

				var B = p[X+1]+Y;
				var BA = p[B]+Z;
				var BB = p[B+1]+Z;

				return lerp(w, lerp(v, lerp(u, grad(p[AA  ], x  , y  , z   ),  // AND ADD
				grad(p[BA  ], x-1, y  , z   )), // BLENDED
				lerp(u, grad(p[AB  ], x  , y-1, z   ),  // RESULTS
				grad(p[BB  ], x-1, y-1, z   ))),// FROM  8
				lerp(v, lerp(u, grad(p[AA+1], x  , y  , z-1 ),  // CORNERS
				grad(p[BA+1], x-1, y  , z-1 )), // OF CUBE
				lerp(u, grad(p[AB+1], x  , y-1, z-1 ),
				grad(p[BB+1], x-1, y-1, z-1 ))));
			}

			function fade(t) { return t * t * t * (t * (t * 6 - 15) + 10); }

			function lerp(t, a, b) { return a + t * (b - a); }

			function grad(hash, x, y, z)
			{
				var h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE
				var u = h<8 ? x : y;                 // INTO 12 GRADIENT DIRECTIONS.
				var v = h<4 ? y : h==12||h==14 ? x : z;
				return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
			}

			// fractal sum
			function fBm2d(x, y, octaves)
			{
				var lacunarity = 2.0;
				var gain = 0.5;

				var freq = 1.0;
				var amp = 0.5;

				var sum = 0;
				for(var i=0; i<octaves; i++)
				{
					sum += noise2d(x*freq, y*freq)*amp;
					freq *= lacunarity;
					amp *= gain;
				}
				return sum;
			}

			function fBm3d(x, y, z, octaves)
			{
				var lacunarity = 2.0;
				var gain = 0.5;

				var freq = 1.0;
				var amp = 0.5;

				var sum = 0;
				for(var i=0; i<octaves; i++)
				{
					sum += noise3d(x*freq, y*freq, z*freq)*amp;
					freq *= lacunarity;
					amp *= gain;
				}
				return sum;
			}

			// Turbulence - absolute value of noise summed
			function turbulence2d(x, y, octaves)
			{
				var lacunarity = 2.0;
				var gain = 0.5;

				var sum = 0;
				var freq = 1.0;
				var amp = 1.0;

				for(var i=0; i<octaves; i++)
				{
					sum += Math.abs(noise2d(x*freq, y*freq))*amp;
					freq *= lacunarity;
					amp *= gain;
				}
				return sum;
			}

			function turbulence3d(x, y, z, octaves)
			{
				var lacunarity = 2.0;
				var gain = 0.5;

				var sum = 0;
				var freq = 1.0;
				var amp = 1.0;

				for(var i=0; i<octaves; i++)
				{
					sum += Math.abs(noise3d(x*freq, y*freq, z*freq))*amp;
					freq *= lacunarity;
					amp *= gain;
				}
				return sum;
			}

			// Ridged multifractal
			// See "Texturing & Modeling, A Procedural Approach", Chapter 12
			function ridge(h, offset)
			{
				h = Math.abs(h);
				h = offset - h;
				h = h * h;
				return h;
			}

			function ridgedmf2d(x, y, octaves)
			{
				var lacunarity = 2.0;
				var gain = 0.5;
				var offset = 1.0;

				var sum = 0;
				var freq = 1.0;
				var amp = 0.5;
				var prev = 1.0;

				for(var i=0; i<octaves; i++)
				{
					var n = ridge(noise2d(x*freq, y*freq), offset);
					sum += n*amp*prev;
					prev = n;
					freq *= lacunarity;
					amp *= gain;
				}
				return sum;
			}

			function ridgedmf3d(x, y, z, octaves)
			{
				var lacunarity = 2.0;
				var gain = 0.5;
				var offset = 1.0;

				var sum = 0;
				var freq = 1.0;
				var amp = 0.5;
				var prev = 1.0;

				for(var i=0; i<octaves; i++)
				{
					var n = ridge(noise3d(x*freq, y*freq, z*freq), offset);
					sum += n*amp*prev;
					prev = n;
					freq *= lacunarity;
					amp *= gain;
				}
				return sum;
			}
		</script>
	</head>
	<body onload="main()">

		<canvas id="perlinCanvas"></canvas>

	</body>

</html>